"""剪映自动化控制，主要与自动导出有关"""
import os.path
import time
import shutil
import uiautomation as uia
# from airtest.core.api import *
from enum import Enum
from typing import Optional, Literal, Callable

from combinations.conf import logger
from . import exceptions
from .exceptions import AutomationError
import pyautogui

simulateMove = True


class Export_resolution(Enum):
    """导出分辨率"""
    RES_8K = "8K"
    RES_4K = "4K"
    RES_2K = "2K"
    RES_1080P = "1080P"
    RES_720P = "720P"
    RES_480P = "480P"


class Export_framerate(Enum):
    """导出帧率"""
    FR_24 = "24fps"
    FR_25 = "25fps"
    FR_30 = "30fps"
    FR_50 = "50fps"
    FR_60 = "60fps"


class ControlFinder:
    """控件查找器，封装部分与控件查找相关的逻辑"""

    @staticmethod
    def desc_matcher(target_desc: str, depth: int = 2, exact: bool = False) -> Callable[[uia.Control, int], bool]:
        """根据full_description查找控件的匹配器"""
        target_desc = target_desc.lower()

        def matcher(control: uia.Control, _depth: int) -> bool:
            if _depth != depth:
                return False
            full_desc: str = control.GetPropertyValue(30159).lower()
            return (target_desc == full_desc) if exact else (target_desc in full_desc)

        return matcher

    @staticmethod
    def class_name_matcher(class_name: str, depth: int = 1, exact: bool = False) -> Callable[[uia.Control, int], bool]:
        """根据ClassName查找控件的匹配器"""
        class_name = class_name.lower()

        def matcher(control: uia.Control, _depth: int) -> bool:
            if _depth != depth:
                return False
            curr_class_name: str = control.ClassName.lower()
            return (class_name == curr_class_name) if exact else (class_name in curr_class_name)

        return matcher


class Jianying_controller:
    """剪映控制器"""

    app: uia.WindowControl
    """剪映窗口"""
    app_status: Literal["home", "edit", "pre_export"]

    def __init__(self):
        """初始化剪映控制器, 此时剪映应该处于目录页"""
        self.get_window()
        self.current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

    def export_draft(self, draft_name: str, output_path: Optional[str] = None, *,
                     resolution: Optional[Export_resolution] = None,
                     framerate: Optional[Export_framerate] = None,
                     timeout: float = 2000,
                     zi_mu: bool = False,
                     kou_tu: bool = False) -> None:
        """导出指定的剪映草稿, **目前仅支持剪映6及以下版本**

        **注意: 需要确认有导出草稿的权限(不使用VIP功能或已开通VIP), 否则可能陷入死循环**

        Args:
            draft_name (`str`): 要导出的剪映草稿名称
            output_path (`str`, optional): 导出路径, 支持指向文件夹或直接指向文件, 不指定则使用剪映默认路径.
            resolution (`Export_resolution`, optional): 导出分辨率, 默认不改变剪映导出窗口中的设置.
            framerate (`Export_framerate`, optional): 导出帧率, 默认不改变剪映导出窗口中的设置.
            timeout (`float`, optional): 导出超时时间(秒), 默认为20分钟.
            kou_tu
            zi_mu
        Raises:
            `DraftNotFound`: 未找到指定名称的剪映草稿
            `AutomationError`: 剪映操作失败
        """
        logger.info(f"开始导出 {draft_name} 至 {output_path}")
        self.get_window()
        self.switch_to_home()

        # 点击对应草稿
        draft_name_text = self.app.TextControl(
            searchDepth=2,
            Compare=ControlFinder.desc_matcher(f"HomePageDraftTitle:{draft_name}", exact=True)
        )

        if not draft_name_text.Exists(0):
            raise exceptions.DraftNotFound(f"未找到名为{draft_name}的剪映草稿")
        draft_btn = draft_name_text.GetParentControl()
        assert draft_btn is not None
        draft_btn.Click(simulateMove=simulateMove)
        time.sleep(2)
        self.get_window()

        # init_device("Windows")

        # touch(Template(os.path.join(self.current_dir,"combinations/rpa/template/开始识别.png")))
        # os.chdir(os.path.join(self.current_dir, "combinations/rpa\\template"))
        # 截图不支持中文
        if zi_mu:
            print("开始字幕识别")

            self.click_button_by_move(os.path.join(self.current_dir, "combinations/rpa/template/ZiMu.png"))
            #
            # touch(Template(os.path.join(self.current_dir,"combinations/rpa/template/开始识别.png")))
            time.sleep(2)

            self.click_button_by_move(os.path.join(self.current_dir, "combinations/rpa/template/ShiBie.png"))

            time.sleep(5)

        if kou_tu:
            try:
                print("开始抠图")
                self.click_button_by_move(os.path.join(self.current_dir, "combinations/rpa/template/KouTuGuiDao.png"))

                time.sleep(2)
                # touch(Template(os.path.join(self.current_dir,"combinations/rpa/template/抠像.png")))
                self.click_button_by_move(os.path.join(self.current_dir, "combinations/rpa/template/KouXiang.png"))

                # touch(Template(os.path.join(self.current_dir, "combinations/rpa/template/智能抠像.png")))
                self.click_button_by_move(
                    os.path.join(self.current_dir, "combinations/rpa/template/ZhiNengKouXiang.png"))
                # 移动到智能抠像选择框
                time.sleep(1)
                self.mouse_move_offset(-40, 0)

                time.sleep(10)
            except Exception as e:

                print("出现异常，下一步", e)
        print("开始导出")
        time.sleep(5)

        export_btn = self.app.TextControl(searchDepth=2,
                                          Compare=ControlFinder.desc_matcher("MainWindowTitleBarExportBtn"))
        if not export_btn.Exists(0):
            # raise AutomationError("未在编辑窗口中找到导出按钮")
            # 点击导出按钮
            # touch(Template(os.path.join(self.current_dir, "combinations/rpa/template/导出.png")))
            try:
                self.click_button(os.path.join(self.current_dir, "combinations/rpa/template/DaoChu.png"))
            except  Exception as e:
                try:
                    self.click_button(os.path.join(self.current_dir, "combinations/rpa/template/DaoChu-2.png"))
                except Exception as e:
                    export_btn = self.app.TextControl(searchDepth=2,
                                                      Compare=ControlFinder.desc_matcher("MainWindowTitleBarExportBtn"))
                    export_btn.Click(simulateMove=simulateMove)
        else:
            export_btn.Click(simulateMove=simulateMove)
        time.sleep(10)
        self.get_window()

        # 获取原始导出路径（带后缀名）
        export_path_sib = self.app.TextControl(searchDepth=2, Compare=ControlFinder.desc_matcher("ExportPath"))
        if not export_path_sib.Exists(0):
            retry = 0
            while True:
                time.sleep(3)
                export_path_sib = self.app.TextControl(searchDepth=2, Compare=ControlFinder.desc_matcher("ExportPath"))
                logger.warning(f"导出按钮扫描，正在进行第 {retry + 1} 次重试...")
                retry += 1
                if retry > 7:
                    break
                if export_path_sib.Exists(0):
                    break

        if not export_path_sib.Exists(0):
            # raise AutomationError("未找到导出路径框")
            # self.click_button(os.path.join(self.current_dir, "combinations/rpa/template/XiuGai-QuXiao.png"))
            self.click_button(os.path.join(self.current_dir, "combinations/rpa/template/DaoChu.png"))
            # touch(Template(os.path.join(self.current_dir,"combinations/rpa/template/草稿修改-取消按钮.png")))
            # touch(Template(os.path.join(self.current_dir,"combinations/rpa/template/导出.png")))
            time.sleep(5)

        if export_path_sib.Exists(0):
            export_path_text = export_path_sib.GetSiblingControl(lambda ctrl: True)
            assert export_path_text is not None
            export_path = export_path_text.GetPropertyValue(30159)

        # 设置分辨率
        if resolution is not None:
            setting_group = self.app.GroupControl(searchDepth=1,
                                                  Compare=ControlFinder.class_name_matcher(
                                                      "PanelSettingsGroup_QMLTYPE"))
            if not setting_group.Exists(0):
                print("未找到导出设置组，开始重试")
                retry = 0
                while True:
                    time.sleep(10)
                    setting_group = self.app.GroupControl(searchDepth=1,
                                                          Compare=ControlFinder.class_name_matcher(
                                                              "PanelSettingsGroup_QMLTYPE"))
                    logger.warning(f"导出设置组扫描，正在进行第 {retry + 1} 次重试...")
                    retry += 1
                    if setting_group.Exists(0):
                        break
                    if retry > 7:
                        break

            # resolution_btn = setting_group.TextControl(searchDepth=2,
            #                                            Compare=ControlFinder.desc_matcher("ExportSharpnessInput"))
            # if not resolution_btn.Exists(0.5):
            #     raise AutomationError("未找到导出分辨率下拉框")
            # resolution_btn.Click(simulateMove=simulateMove)
            # time.sleep(0.5)
            # resolution_item = self.app.TextControl(
            #     searchDepth=2, Compare=ControlFinder.desc_matcher(resolution.value)
            # )
            # if not resolution_item.Exists(0.5):
            #     raise AutomationError(f"未找到{resolution.value}分辨率选项")
            # resolution_item.Click(simulateMove=simulateMove)
            # time.sleep(0.5)

        # 设置帧率
        if framerate is not None:
            setting_group = self.app.GroupControl(searchDepth=1,
                                                  Compare=ControlFinder.class_name_matcher(
                                                      "PanelSettingsGroup_QMLTYPE"))
            if not setting_group.Exists(0):
                raise AutomationError("未找到导出设置组")
            framerate_btn = setting_group.TextControl(searchDepth=2,
                                                      Compare=ControlFinder.desc_matcher("FrameRateInput"))
            if not framerate_btn.Exists(0.5):
                raise AutomationError("未找到导出帧率下拉框")
            framerate_btn.Click(simulateMove=simulateMove)
            time.sleep(0.5)
            framerate_item = self.app.TextControl(
                searchDepth=2, Compare=ControlFinder.desc_matcher(framerate.value)
            )
            if not framerate_item.Exists(0.5):
                raise AutomationError(f"未找到{framerate.value}帧率选项")
            framerate_item.Click(simulateMove=simulateMove)
            time.sleep(0.5)

        # 点击导出
        export_btn = self.app.TextControl(searchDepth=2, Compare=ControlFinder.desc_matcher("ExportOkBtn", exact=True))
        if not export_btn.Exists(0):
            raise AutomationError("未在导出窗口中找到导出按钮")
        export_btn.Click(simulateMove=simulateMove)
        time.sleep(5)

        # 等待导出完成
        st = time.time()
        while True:
            self.get_window()
            if self.app_status != "pre_export": continue

            succeed_close_btn = self.app.TextControl(searchDepth=2,
                                                     Compare=ControlFinder.desc_matcher("ExportSucceedCloseBtn"))
            if succeed_close_btn.Exists(0):
                succeed_close_btn.Click(simulateMove=simulateMove)
                break

            if time.time() - st > timeout:
                raise AutomationError("导出超时, 时限为%d秒" % timeout)

            time.sleep(1)
        time.sleep(2)

        # 回到目录页
        self.get_window()
        self.switch_to_home()
        time.sleep(2)

        # 复制导出的文件到指定目录
        if output_path is not None:
            shutil.move(export_path, output_path)

        logger.info(f"导出 {draft_name} 至 {output_path} 完成")

    def switch_to_home(self) -> None:
        """切换到剪映主页"""
        if self.app_status == "home":
            return
        if self.app_status != "edit":
            raise AutomationError("仅支持从编辑模式切换到主页")
        # print("切换到主页")
        close_btn = self.app.GroupControl(searchDepth=1, ClassName="TitleBarButton", foundIndex=3)
        close_btn.Click(simulateMove=simulateMove)
        self.get_window()

    def get_window(self, max_retries=10, duration=10) -> None:
        """寻找剪映窗口并置顶"""
        if hasattr(self, "app") and self.app.Exists(0):
            self.app.SetTopmost(False)
        # self.app = uia.WindowControl(searchDepth=1, Compare=self.__jianying_window_cmp)

        attempt = 0
        while attempt < max_retries:
            self.app = uia.WindowControl(searchDepth=1, Compare=self.__jianying_window_cmp)

            if self.app.Exists(0):
                # 成功找到窗口，退出循环
                break
            time.sleep(duration)

            logger.warning(f"[尝试 {attempt + 1}/{max_retries}] 剪映窗口未找到，正在重试...")
            attempt += 1

        if not self.app.Exists(0):
            raise AutomationError("剪映窗口未找到")

        # 寻找可能存在的导出窗口
        export_window = self.app.WindowControl(searchDepth=1, Name="导出")
        if export_window.Exists(0):
            self.app = export_window
            self.app_status = "pre_export"

        self.app.SetActive()
        self.app.SetTopmost()
        # print("已经找到剪印窗口")

    def __jianying_window_cmp(self, control: uia.WindowControl, depth: int) -> bool:
        if control.Name != "剪映专业版":
            return False
        if "HomePage".lower() in control.ClassName.lower():
            self.app_status = "home"
            return True
        if "MainWindow".lower() in control.ClassName.lower():
            self.app_status = "edit"
            return True
        return False

    @staticmethod
    def click_button(png_path, retry_times=10):
        """点击指定图片路径的按钮，失败时重试"""
        for attempt in range(1 + retry_times):  # 最初一次 + retry_times次重试
            # 当你在调用 pyautogui.locateOnScreen() 时设置参数 grayscale=True，PyAutoGUI 会在内部将屏幕截图和目标图像（即 png_path）都转换为灰度图像，再进行匹配。
            try:
                button_location = pyautogui.locateOnScreen(png_path, confidence=0.7, grayscale=True)
                if button_location is not None:
                    # 计算中心点并点击
                    button_center = (
                        button_location.left + button_location.width // 2,
                        button_location.top + button_location.height // 2,
                    )
                    pyautogui.click(button_center)
                    return  # 成功后直接返回
            except pyautogui.ImageNotFoundException:
                time.sleep(3)
                logger.warning(f"[ImageNotFound] 未找到按钮图像: {png_path}，正在进行第 {attempt + 1} 次重试...")
            except Exception as e:
                logger.error(f"发生未知错误: {e}", exc_info=True)
                if attempt < retry_times:
                    logger.warning(f"正在进行第 {attempt + 1} 次重试...")
                else:
                    raise AutomationError(f"点击按钮失败: {png_path}") from e

            if attempt < retry_times:
                time.sleep(1)  # 每次重试之间等待1秒
            else:
                raise AutomationError(f"未找到按钮图像: {png_path}，已达到最大重试次数")

    @staticmethod
    def click_button_by_move(png_path):
        # confidence 识别灵敏度
        button_location = pyautogui.locateOnScreen(png_path, confidence=0.7)
        # 检查是否找到图像
        if button_location is not None:
            # 使用Box对象的属性获取中心点
            pyautogui.moveTo(button_location.left + button_location.width // 2,
                             button_location.top + button_location.height // 2)
            # 点击该中心点
            pyautogui.click()
        else:
            raise AutomationError("未找到按钮")

    @staticmethod
    def mouse_move_offset(x_offset, y_offset):

        num_seconds = 1
        # pyautogui.moveRel(0, 50)  # 向下移动50
        # pyautogui.moveRel(30, 0, 2)  # 向右移动30
        # pyautogui.moveRel(30, None)  # 向右移动30
        pyautogui.moveRel(x_offset, y_offset, duration=num_seconds)
        pyautogui.click()
